{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'1.0.0'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "import math\n",
    "import numpy as np\n",
    "from visdom import Visdom\n",
    "import time\n",
    "torch.__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4.2.1 使用Visdom在 PyTorch 中进行可视化\n",
    "Visdom是Facebook在2017年发布的一款针对PyTorch的可视化工具。[官网](https://github.com/facebookresearch/visdom),visdom由于其功能简单，一般会被定义为服务器端的matplot，也就是说我们可以直接使用python的控制台模式进行开发并在服务器上执行，将一些可视化的数据传送到Visdom服务上，通过Visdom服务进行可视化"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 安装\n",
    "Visdom的安装很简单，直接使用命令`pip install visdom`安装即可。\n",
    "在安装完成后，使用命令`python -m visdom.server` 在本地启动服务器，启动后会提示`It's Alive! You can navigate to http://localhost:8097` 这就说明服务已经可用，我们打开浏览器，输入`http://localhost:8097` 即可看到页面。\n",
    "\n",
    "端口8097是默认的端口可以在启动命令后加 `-port`参数指定端口，常用的参数还有 `--hostname`，`-base_url`等"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 坑\n",
    "Visdom的服务在启动时会自动下载一些静态文件，这里坑就来了，因为某些无法描述的原因，导致下载会失败，比如类似这样的提示 `ERROR:root:Error 404 while downloading https://unpkg.com/layout-bin-packer@1.4.0` 就说明静态文件没有下载完全，这样有可能就会打不开或者页面中没有菜单栏，那么需要手动进行下载，这里我打包了一份正常的静态文件，直接复制到`Lib\\site-packages\\visdom`中即可。\n",
    "\n",
    "如果不知道conda的环境目录在哪里，可以使用`conda env list` 查看\n",
    "\n",
    "感谢CSDN的伙伴提供的缺失文件，原文[这里](https://blog.csdn.net/qq_36941368/article/details/82288154)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基本概念\n",
    "### Environments\n",
    "Environments的作用是对可视化区域进行分区，每个用户都会有一个叫做main的默认分区，如图所示:\n",
    "![](1.PNG)\n",
    "在程序指定的情况下，默认的图表都会放到这里面"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Panes\n",
    "Panes是作为每一个可视化图表的容器，可以使用生成的图表，图片，文本进行填充，我们可以对Panes进行拖放，删除，调整大小和销毁等操作：\n",
    "![](2.PNG)\n",
    "Panes和Environments是一对多的关系，即一个Environments可以包含多个Panes\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### VIEW\n",
    "在对Panes进行调整后，可以通过VIEW对状态进行管理：\n",
    "![](3.PNG)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 可视化接口\n",
    "Visdom是由Plotly 提供的可视化支持，所以提供一下可视化的接口:\n",
    "- vis.scatter : 2D 或 3D 散点图\n",
    "- vis.line : 线图\n",
    "- vis.stem : 茎叶图\n",
    "- vis.heatmap : 热力图\n",
    "- vis.bar : 条形图\n",
    "- vis.histogram: 直方图\n",
    "- vis.boxplot : 箱型图\n",
    "- vis.surf : 表面图\n",
    "- vis.contour : 轮廓图\n",
    "- vis.quiver : 绘出二维矢量场\n",
    "- vis.image : 图片\n",
    "- vis.text : 文本\n",
    "- vis.mesh : 网格图\n",
    "- vis.save : 序列化状态"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用\n",
    "### 绘制简单的图形\n",
    "这里我们使用官方的DEMO来做样例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "env = Visdom() \n",
    "assert env.check_connection() #测试一下链接，链接错误的话会报错"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里生成sin和cos两条曲线数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "Y = np.linspace(0, 2 * math.pi, 70)\n",
    "X = np.column_stack((np.sin(Y), np.cos(Y)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用茎叶图展示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'window_36f18bc34b4992'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "env.stem(\n",
    "        X=X,\n",
    "        Y=Y,\n",
    "        opts=dict(legend=['Sine', 'Cosine'])\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以通过env参数指定Environments，如果名称包含了下划线`_`那么visdom会跟根据下划线分割并自动分组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "envtest = Visdom(env='test_mesh')\n",
    "assert envtest.check_connection()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "生成一个网格图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'window_36f18bc533e990'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = [0, 0, 1, 1, 0, 0, 1, 1]\n",
    "y = [0, 1, 1, 0, 0, 1, 1, 0]\n",
    "z = [0, 0, 0, 0, 1, 1, 1, 1]\n",
    "X = np.c_[x, y, z]\n",
    "i = [7, 0, 0, 0, 4, 4, 6, 6, 4, 0, 3, 2]\n",
    "j = [3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3]\n",
    "k = [0, 7, 2, 3, 6, 7, 1, 1, 5, 5, 7, 6]\n",
    "Y = np.c_[i, j, k]\n",
    "envtest.mesh(X=X, Y=Y, opts=dict(opacity=0.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 更新损失函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在训练的时候我们每一批次都会打印一下训练的损失和测试的准确率，这样展示的图表是需要动态增加数据的，下面我们来模拟一下这种情况"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "x,y=0,0\n",
    "env2 = Visdom()\n",
    "pane1= env2.line(\n",
    "    X=np.array([x]),\n",
    "    Y=np.array([y]),\n",
    "    opts=dict(title='dynamic data'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 0.0\n",
      "1 1.5\n",
      "3 5.25\n",
      "6 12.375\n",
      "10 24.5625\n",
      "15 44.34375\n",
      "21 75.515625\n",
      "28 123.7734375\n",
      "36 197.66015625\n",
      "45 309.990234375\n"
     ]
    }
   ],
   "source": [
    "for i in range(10):\n",
    "    time.sleep(1) #每隔一秒钟打印一次数据\n",
    "    x+=i\n",
    "    y=(y+i)*1.5\n",
    "    print(x,y)\n",
    "    env2.line(\n",
    "        X=np.array([x]),\n",
    "        Y=np.array([y]),\n",
    "        win=pane1,#win参数确认使用哪一个pane\n",
    "        update='append') #我们做的动作是追加，除了追加意外还有其他方式，这里我们不做介绍了"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在运行完上述程序时，切换到visdom，看看效果吧\n",
    "\n",
    "visdom的基本用法介绍完毕，下一节介绍更加强大的 tensorboardx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorch 1.0",
   "language": "python",
   "name": "pytorch1"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
